package com.siteseat.common.spring;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;

/**
 *
 */
public class ModularBeanFactory extends DefaultListableBeanFactory {

  private ExposedBeanSet exposedByModules;  
  
  /**
   *
   *
   * @param super
   */
  public ModularBeanFactory( ExposedBeanSet exposedByModules, BeanFactory parentBeanFactory ) {
    super( parentBeanFactory  );
    this.exposedByModules = exposedByModules;
  }

  /**
   * {@inheritDoc}
   */
  @SuppressWarnings( "unchecked" )
  @Override
  public Object autowire( Class beanClass, int autowireMode, boolean dependencyCheck ) throws BeansException {
    return super.autowire( beanClass, autowireMode, dependencyCheck );
  }
  
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean containsBean( String name ) {
    if( exposedByModules.contains( name ) ) {
      return true;
    }
    return super.containsBean( name );
  }
  
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean containsBeanDefinition( String beanName ) {
    if( exposedByModules.containsDefinition( beanName ) ) {
      return true;
    }
    return super.containsBeanDefinition( beanName );
  }  
  
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean containsSingleton( String beanName ) {
    ExposedBean b = exposedByModules.findDescription( beanName );
    if( b != null ) {
      return b.isSingleton();
    }
    return super.containsSingleton( beanName );
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public <A extends Annotation> A findAnnotationOnBean( String beanName, Class<A> annotationType ) {
    A r = exposedByModules.findAnnotation( beanName, annotationType );
    if( r != null ) {
      return r;
    }
    return super.findAnnotationOnBean( beanName, annotationType );
  }
  
  /**
   * {@inheritDoc}
   */
  @Override
  public String[] getAliases( String name ) {
    String[] r = exposedByModules.getAliases( name );
    if( r != null && r.length > 0 ) {
      return r;
    }
    return super.getAliases( name );
  }
  

  /**
   * {@inheritDoc}
   */
  @Override
  public <T> T getBean( Class<T> requiredType ) throws BeansException {
    T r = exposedByModules.get( requiredType );
    if( r != null ) {
      return r;
    }    
    return super.getBean( requiredType );
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public <T> T getBean( String name, Class<T> requiredType ) throws BeansException {
    T r = exposedByModules.getBean( name, requiredType );
    if( r != null ) {
      return r;
    }
    return super.getBean( name, requiredType );
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Object getBean( String name, Object... args ) throws BeansException {
    Object r = exposedByModules.getBean( name, args );
    if( r != null ) {
      return r;
    }
    return super.getBean( name, args ); 
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Object getBean( String name ) throws BeansException {
    Object r = exposedByModules.getBean( name );
    if( r != null ) {
      return r;
    }
    return super.getBean( name );
  }


  /**
   * {@inheritDoc}
   */
  @Override
  public int getBeanDefinitionCount() {
    return exposedByModules.getBeanDefinitionCount() + super.getBeanDefinitionCount();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String[] getBeanDefinitionNames() {
    List<String> c = new ArrayList<String>( exposedByModules.getBeanDefinitionNames() );
    c.addAll( Arrays.asList( super.getBeanDefinitionNames() ) );
    return c.toArray( new String[c.size()] );
  }
  
  /**
   * {@inheritDoc}
   */
  @SuppressWarnings( "unchecked" )
  @Override
  public String[] getBeanNamesForType( Class type, boolean includeNonSingletons, boolean allowEagerInit ) {
    String[] x = super.getBeanNamesForType( type, includeNonSingletons, allowEagerInit );
    List<String> r = exposedByModules.getBeanNamesForType( type, includeNonSingletons, allowEagerInit );
    r.addAll( Arrays.asList( x ) );
    return r.toArray( new String[r.size()] );
  }

  /**
   * {@inheritDoc}
   */
  @SuppressWarnings( "unchecked" )
  @Override
  public String[] getBeanNamesForType( Class type ) {
    Collection c = exposedByModules.getBeanNamesForType( type );
    c.addAll( Arrays.asList( super.getBeanNamesForType( type ) ) );
    return (String[])c.toArray( new String[c.size()] );
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public <T> Map<String, T> getBeansOfType( Class<T> type, boolean includeNonSingletons, boolean allowEagerInit )
          throws BeansException {
    Map<String, T> r = super.getBeansOfType( type, includeNonSingletons, allowEagerInit );
    r.putAll( exposedByModules.getBeansOfType( type, includeNonSingletons, allowEagerInit ) );
    return r;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public <T> Map<String, T> getBeansOfType( Class<T> type ) throws BeansException {
    Map<String,T> r = super.getBeansOfType( type );
    r.putAll( exposedByModules.getBeansOfType( type ) );
    return r;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Map<String, Object> getBeansWithAnnotation( Class<? extends Annotation> annotationType ) throws BeansException {
    Map<String, Object> r = super.getBeansWithAnnotation( annotationType );
    r.putAll( exposedByModules.getBeansWithAnnotation( annotationType ) );
    return r;
  }
  
  /**
   * {@inheritDoc}
   */
  @Override
  public String[] getDependenciesForBean( String beanName ) {
    ExposedBean b = exposedByModules.findDescription( beanName );
    if( b != null ) {
      return b.getDependencies();
    }
    return super.getDependenciesForBean( beanName );
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String[] getDependentBeans( String beanName ) {
    ExposedBean b = exposedByModules.findDescription( beanName );
    if( b != null ) {
      return b.getDependentBeans();
    }
    return super.getDependentBeans( beanName );
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public BeanDefinition getMergedBeanDefinition( String beanName ) throws NoSuchBeanDefinitionException {
    BeanDefinition r = exposedByModules.getMergedBeanDefinition( beanName );
    if( r != null ) {
      return r;
    }
    return super.getMergedBeanDefinition( beanName );
  }
  
  /**
   * {@inheritDoc}
   */
  @Override
  public Object getSingleton( String beanName ) {
    Object r = exposedByModules.getSingleton( beanName );
    if( r != null ) {
      return r;
    }
    return super.getSingleton( beanName );
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public int getSingletonCount() {
    return exposedByModules.getSingletonCount() + super.getSingletonCount();
  }
  
  /**
   * {@inheritDoc}
   */
  @Override
  public String[] getSingletonNames() {
    List<String> r = exposedByModules.getSingletonNames();
    r.addAll( Arrays.asList( super.getSingletonNames() ) );
    return r.toArray( new String[r.size()] );
  }

  
  /**
   * {@inheritDoc}
   */
  @Override
  public Class<?> getType( String name ) throws NoSuchBeanDefinitionException {
    Class<?> r = exposedByModules.getType( name );
    if( r != null ) {
      return r;
    }
    return super.getType( name );
  }
  
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isCurrentlyInCreation( String beanName ) {
    ExposedBean b = exposedByModules.findDescription( beanName );
    if( b != null ) {
      return b.isCurrentlyInCreation();
    }
    return super.isCurrentlyInCreation( beanName );
  }
  
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isFactoryBean( String name ) throws NoSuchBeanDefinitionException {
    ExposedBean b = exposedByModules.findDescription( name );
    if( b != null ) {
      return b.isFactoryBean();
    }
    return super.isFactoryBean( name );
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isPrototype( String name ) throws NoSuchBeanDefinitionException {
    ExposedBean b = exposedByModules.findDescription( name );
    if( b != null ) {
      return b.isPrototype();
    }    
    return super.isPrototype( name );
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isSingleton( String name ) throws NoSuchBeanDefinitionException {
    ExposedBean b = exposedByModules.findDescription( name );
    if( b != null ) {
      return b.isSingleton();
    }    
    return super.isSingleton( name );
  }
  
  /**
   * {@inheritDoc}
   */
  @SuppressWarnings( "unchecked" )
  @Override
  public boolean isTypeMatch( String name, Class targetType ) throws NoSuchBeanDefinitionException {
    ExposedBean b = exposedByModules.findDescription( name );
    if( b != null ) {
      return b.isTypeMatch( targetType );
    }        
    return super.isTypeMatch( name, targetType );
  }
  
  /**
   * {@inheritDoc}
   */
  @Override
  public BeanDefinition getBeanDefinition( String beanName ) throws NoSuchBeanDefinitionException {
    BeanDefinition b = exposedByModules.getBeanDefinition( beanName );
    if( b != null ) {
      return b;
    }  
    
    return super.getBeanDefinition( beanName );
  }
 
  /**
   * {@inheritDoc}
   */
  @Override
  public BeanFactory getParentBeanFactory() {
    return super.getParentBeanFactory();
  }
  
  
}
